import React from 'react'
import ReactDOM from 'react-dom'

// 当调用 useState 的时候，会返回形如 (变量, 函数) 的一个元祖。并且 state 的初始值就是外部调用 useState 的时候，传入的参数。
// 借助闭包实现一个雏形 useState
// let state
// function useState(initialState, newState) {
//   state = state || initialState

//   function setState(newState) {
//     state = newState
//     render()
//   }

//   return [state, setState]
// }

// 前面 useState 的简单实现里，初始的状态是保存在一个全局变量中的。以此类推，多个状态，应该是保存在一个专门的全局容器中。
// 这个容器，就是一个朴实无华的 Array 对象。具体过程如下：
// 第一次渲染时候，根据 useState 顺序，逐个声明 state 并且将其放入全局 Array 中。每次声明 state，都要将 cursor 增加 1。
// 更新 state，触发再次渲染的时候。cursor 被重置为 0。按照 useState 的声明顺序，依次拿出最新的 state 的值，视图更新。

let states = []
let cursor = 0
function useState(initialState, newState) {
  const currentCursor = cursor
  states[currentCursor] = states[currentCursor] || initialState

  function setState(newState) {
    states[currentCursor] = newState
    render()
  }

  ++cursor // update cursor
  // console.log(cursor)
  return [states[currentCursor], setState]
}




// useEfect 实现 还是利用Array + Cursor原理
const allDeps = []
let effectCursor = 0
function useEfect(cb, deps) {
  if (!allDeps[effectCursor]) {
    // 初次渲染 赋值 + 调用回掉函数
    allDeps[effectCursor] = deps
    ++effectCursor
    cb()
    return
  }

  let currenteffectCursor = effectCursor
  let rawDep = allDeps[currenteffectCursor]
  // 检测依赖项是否发生变化，发生，重新render
  const isChange = rawDep.some((dep, idx) => dep !== deps[idx])
  if (isChange) {
    cb()
  }
  ++effectCursor
}



let tag = true;
const App = () => {
  const [num, setNum] = useState(0);

  // 只有初次渲染才执行
  // if (tag) {
  //   const [num1, setNum1] = useState(1);
  //   tag = false
  // }

  // 此时，如果想在循环、判断等不在函数组件顶部的地方使用 Hook
  // 由于在条件判断的逻辑中，重置了tag=false，因此此后的渲染不会再进入条件判断语句。
  // 看起来好像没有问题？但是，由于 useState 是基于 Array+Cursor 来实现的，第一次渲染时候，
  // state 和 cursor 的对应关系如下：
  // 变量名     cursor
  // num        0
  // num1       1
  // num2       2
  // 当点击事件触发再次渲染，并不会进入条件判断中的 useState。所以，cursor=2 的时候对应的变量是 num2。
  // 而其实 num2 对应的 cursor 应该是 3。就会导致setNum2并不起作用
  // 因此 解决了「🤔️ 为什么不能在循环、判断内部使用 Hook」。在使用 Hook 的时候，请在函数组件顶部使用！


  const [num2, setNum2] = useState(2);
  const [num3, setNum3] = useState(3);

  // 多次触发, 每次点击按钮，都会触发 setNum 函数, 副作用检测到 num 变化，会自动调用回调函数
  useEfect(() => {
    console.log("num update: ", num);
  }, [num])

  // 仅第一次触发, 只会在compoentDidMount时，触发一次, 副作用函数不会多次执行
  useEfect(() => {
    console.log("num3 update: ", num3);
  }, [num3])

  return (
    <div>
      <div>
        <div>num: {num}</div>
        <button onClick={() => setNum(num + 1)}>加 1</button>
        <button onClick={() => setNum(num - 1)}>减 1</button>
      </div>
      <div>
        <div>num1: {num2}</div>
        <button onClick={() => setNum2(num2 * 2)}>扩大一倍</button>
        <button onClick={() => setNum2(num2 / 2)}>缩小一倍</button>
      </div>
    </div>
  )
}

function render() {
  ReactDOM.render(<App />, document.getElementById('root'))
  cursor = 0 // 重置 cursor
  effectCursor = 0; // 注意将 effectCursor 重置为0
}

render() // 初次渲染
